﻿/* MVT Charts for Silverlight - Version 1
 * 2008 by Mario Meir-Huber
 * mailto: i-mameir@microsoft.com
 *         mario@meirhuber.de
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using ChartLibrary.Base;
using ChartLibrary.Data;
using System.Diagnostics;
using System.Globalization;

/*
 * ChartLibrary.Charts is the Namespace for all charts.
 */
namespace ChartLibrary.Charts
{
    /// <summary>
    /// A simple Pie chart
    /// </summary>
    public partial class SimplePieChart : ChartControl
    {
        public event ItemHandler ItemMouseEnter;  //occurs when the mouse enters the item
        public event ItemHandler ItemMouseLeave;  //occurs when the mouse leaves the item
        public event ItemHandler ItemClicked;     //occurs when a mouse click is performed on the item
            
        private List<double> values;        //holds a list of doubles
        private double total = 0.0;         //is the total value

        private double rad = 0.0;       //this is the used ratio
        private double startX = 0.0;    //the x position to start
        private double startY = 0.0;    //the y position to start

        private double deg_start = 0.0; //starting degree

        private List<Shape> _delPie = null; //stores information on all pie charts 

        /// <summary>
        /// Displays a simple Pie Chart
        /// </summary>
        public SimplePieChart()
        {
            LayoutRoot.Loaded += new RoutedEventHandler(RoundChart_Loaded);
            LayoutRoot.SizeChanged += new SizeChangedEventHandler(RoundChart_SizeChanged);

            this.DrawAreaChanged += new StateChangedEvent(SimplePieChart_DrawAreaChanged);

            _delPie = new List<Shape>();
        }

        /// <summary>
        /// Occurs when the drawable area was changed
        /// </summary>
        void SimplePieChart_DrawAreaChanged()
        {
            CalculateMetrics(); 

            RedrawChart();
        }

        /// <summary>
        /// Occurs when the size was changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void RoundChart_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            CalculateMetrics();

            RedrawChart();
        }

        /// <summary>
        /// Recalculates the Metrics
        /// </summary>
        private void CalculateMetrics()
        {
            if (AreaHeight > AreaWidth)
            {
                rad = AreaWidth / 2 - 20;
                startX = AreaWidth / 2 - 10 + AreaStartX;
                startY = AreaHeight / 2 - 10 + AreaStartY;
            }
            else
            {
                rad = AreaHeight / 2 - 20;
                startX = AreaWidth / 2 - 10 + AreaStartX;
                startY = AreaHeight / 2 - 10 + AreaStartY;
            }

            for (int i = 0; i < LayoutRoot.Children.Count; i++)
			{
			    if (LayoutRoot.Children[i] is Polygon)
                {
                    LayoutRoot.Children.RemoveAt(i);
                }
			} 
        }

        /// <summary>
        /// Occurs when the mouse enters an item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnItemLeave(object sender, MouseEventArgs e)
        {
            if (null != ItemMouseLeave) ItemMouseLeave(sender, new ItemEventArgs());
        }

        /// <summary>
        /// Occurs when an item was clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnItemClick(object sender, MouseButtonEventArgs e)
        {
            if (null != ItemClicked)
            {
                ItemClicked(sender, new ItemEventArgs());
            }
        }

        /// <summary>
        /// Occurs when the mouse enters an item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnItemEnter(object sender, MouseEventArgs e)
        {
            if (null != ItemMouseEnter) ItemMouseEnter(sender, new ItemEventArgs());
        }

        /// <summary>
        /// Occurs when the Chart was loaded
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void RoundChart_Loaded(object sender, RoutedEventArgs e)
        {
            UpdateDataControler();

            RedrawChart();
        }

        /// <summary>
        /// Redraws the Pie Chart
        /// </summary>
        private void RedrawChart()
        {
            Polygon tmpPoly;
            double degmax = 0.0;

            TransformGroup rtg = new TransformGroup();
            RotateTransform rot = new RotateTransform();
            ScaleTransform scale = new ScaleTransform();

            Storyboard st;

            CalculateMetrics();

            foreach (Shape sht in _delPie)
            {
                LayoutRoot.Children.Remove(sht);
            }

            for (int i = 0; i < Data.Count; i++)
            {
                degmax = 360 / total * values[i];

                tmpPoly = Pie(deg_start, degmax + deg_start);

                tmpPoly.SetValue(Canvas.NameProperty, "_pie_" + i.ToString());

                rtg.Children.Add(rot);
                rtg.Children.Add(scale);

                tmpPoly.RenderTransform = rtg;

                if (Animation == Animation.FxOpacityAsynchrounous)
                {
                    tmpPoly.Opacity = 0.0;
                }

                _delPie.Add(tmpPoly);

                tmpPoly.Fill = Data[i].Fill;

                LayoutRoot.Children.Add(tmpPoly);

                tmpPoly.MouseEnter += new MouseEventHandler(OnItemEnter);
                tmpPoly.MouseLeave += new MouseEventHandler(OnItemLeave);
                tmpPoly.MouseLeftButtonUp += new MouseButtonEventHandler(OnItemClick);

                deg_start += degmax;

                st = AddAnimation((string)tmpPoly.GetValue(Canvas.NameProperty), i,0);

                st.Completed += new EventHandler(st_Completed);

                //LayoutRoot.Resources.Add(st);
                st.Begin();
            }
        }

        void st_Completed(object sender, EventArgs e)
        {
            //LayoutRoot.Resources.Remove((Storyboard)sender);
        }

        /// <summary>
        /// Updates the Data Controler
        /// </summary>
        private void UpdateDataControler()
        {
            values = new List<double>();
            double tmpval = 0.0;
            total = 0.0;

            foreach (DataSource ds in Data)
            {
                tmpval = 0.0;

                while (ds.MoveNext())
                {
                    tmpval += ds.Current.Value;
                }

                values.Add(tmpval);
                total += tmpval;
            }
        }

        /// <summary>
        /// This draws a single Pie
        /// </summary>
        /// <param name="DegreeStartAt"></param>
        /// <param name="Degree"></param>
        /// <returns></returns>
        private Polygon Pie(double DegreeStartAt, double Degree)
        {
            Point ypoint;
            double x = 0.0, y = 0.0;

            Polygon pg = new Polygon();
            pg.Fill = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));

            pg.Points.Add(new Point(startX, startY));

            while (DegreeStartAt <= Degree)
            {
                x = rad * Math.Cos(DegreeStartAt * Math.PI / 180);
                y = rad * Math.Sin(DegreeStartAt * Math.PI / 180);

                DegreeStartAt += 0.1;

                ypoint = new Point(startX + x, startY - y);

                pg.Points.Add(ypoint);
            }

            pg.Points.Add(new Point(startX, startY));

            return pg;
        }

        /// <summary>
        /// adds a new animation to an item
        /// </summary>
        /// <param name="target">target to be animated</param>
        /// <param name="AsynchId">information on an asynch id</param>
        /// <param name="height">endposition to be used</param>
        /// <returns></returns>
        private Storyboard AddAnimation(string target, int AsynchId, double height)
        {
            string anim = "";
            Storyboard retval = null;

            switch (Animation)
            {
                default:
                    {
                        anim = "<Storyboard xmlns=\"http://schemas.microsoft.com/client/2007\"/>";
                        break;
                    }
                case ChartLibrary.Base.Animation.FxDropDown:
                    {
                        anim = "<Storyboard xmlns=\"http://schemas.microsoft.com/client/2007\">" +
                        "<DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleY)\" " +
                        "BeginTime=\"00:00:00\">" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:00\" Value=\"0.0\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:00.3\" Value=\"1.1\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:00.4\" Value=\"1.0\"/>" +
                        "</DoubleAnimationUsingKeyFrames>" +
                        "</Storyboard>";
                        break;
                    }
                case ChartLibrary.Base.Animation.FxRiseUp:
                    {
                        anim = "<Storyboard xmlns=\"http://schemas.microsoft.com/client/2007\">" +
                        "<PointAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.RenderTransformOrigin)\"" +
                        " BeginTime=\"00:00:00\"><SplinePointKeyFrame KeyTime=\"00:00:00\" Value=\"1,1\"/></PointAnimationUsingKeyFrames>" +
                        "<DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleX)\" " +
                        "BeginTime=\"00:00:00\">" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:0\" Value=\"0.0\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:" + (0.1 * AsynchId).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"1.1\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:" + (0.1 * AsynchId + 0.3).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"1.0\"/>" +
                        "</DoubleAnimationUsingKeyFrames>" +
                        "<DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleY)\" " +
                        "BeginTime=\"00:00:00\">" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:0\" Value=\"0.0\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:" + (0.1 * AsynchId).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"1.1\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:" + (0.1 * AsynchId + 0.3).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"1.0\"/>" +
                        "</DoubleAnimationUsingKeyFrames>" +
                        "</Storyboard>";
                        break;
                    }
                case ChartLibrary.Base.Animation.FxOpacity:
                    {
                        anim = "<Storyboard xmlns=\"http://schemas.microsoft.com/client/2007\">" +
                        "<DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.Opacity)\" " +
                        "BeginTime=\"00:00:00\"><SplineDoubleKeyFrame KeyTime=\"00:00:00\" Value=\"0\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:00.5000000\" Value=\"1\"/></DoubleAnimationUsingKeyFrames></Storyboard>";
                        break;
                    }
                case ChartLibrary.Base.Animation.FxOpacityAsynchrounous:
                    {
                        anim = "<Storyboard xmlns=\"http://schemas.microsoft.com/client/2007\">" +
                        "<DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.Opacity)\" " +
                        "BeginTime=\"00:00:00\"><SplineDoubleKeyFrame KeyTime=\"00:00:0" + (0.1 * AsynchId).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"0\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:0" + (0.1 * AsynchId + 0.3).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + " \" Value=\"1\"/></DoubleAnimationUsingKeyFrames></Storyboard>";
                        break;
                    }
            }

            retval = (Storyboard)System.Windows.Markup.XamlReader.Load(anim);

            return retval;
        }
    }
}
